﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Ccr.Core;
using NUnit.Framework;
using XcoAppSpaces.Core.Communication;

namespace test.XcoAppSpaces.Core.Communication
{
	[TestFixture]
	public class RemotePortRegistryTest
	{
		[Test]
		public void RegisterAndResolve()
		{
			using (var reg = new RemotePortRegistry())
			{
				var port = new Port<string>();
				var id = reg.Register(port);
				var resolved = reg.Resolve(id);
				
				Assert.AreEqual(port, resolved);
				Assert.AreEqual(1, reg.Count);
			}
		}

		[Test]
		public void RegisterPermanentlyAndResolve()
		{
			using (var reg = new RemotePortRegistry())
			{
				var port = new Port<string>();
				var id = Guid.NewGuid();
				var registeredId = reg.RegisterPermanently(port, id);
				var resolved = reg.Resolve(registeredId);

				Assert.AreEqual(id, registeredId);
				Assert.AreEqual(port, resolved);
				Assert.AreEqual(1, reg.Count);
			}
		}

		[Test]
		public void PortWithoutRefIsGarbageCollected()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 0;
				var id = reg.Register(new Port<string>());
				Thread.Sleep(100);
				reg.CheckPortExpiration();
				GC.Collect();
				Assert.AreEqual(1, reg.Count); //entry is still there

				var resolved = reg.Resolve(id);

				Assert.IsNull(resolved);
				Assert.AreEqual(0, reg.Count); //entry is removed during resolve
			}
		}

		[Test]
		public void PortWithRefIsNotGarbageCollected()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 0;
				var port = new Port<string>();
				var id = reg.Register(port);
				Thread.Sleep(100);
				reg.CheckPortExpiration();
				var resolved = reg.Resolve(id);

				Assert.AreEqual(port, resolved);
			}
		}

		[Test]
		public void PermanentPortIsNotGarbageCollected()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 0;
				var id = Guid.NewGuid();
				reg.RegisterPermanently(new Port<string>(), id);
				Thread.Sleep(100);
				reg.CheckPortExpiration();
				GC.Collect();
				var resolved = reg.Resolve(id);

				Assert.IsNotNull(resolved);
			}
		}

		[Test]
		public void PortIsNotGarbageCollecteWhileNotExpired()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 10;
				var id = reg.Register(new Port<string>());
				Thread.Sleep(100);
				reg.CheckPortExpiration();
				GC.Collect();
				var resolved = reg.Resolve(id);

				Assert.IsNotNull(resolved);
			}
		}

		[Test]
		public void PortWithoutRefIsStillAvailableBeforeGC()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 0;
				var id = reg.Register(new Port<string>());
				Thread.Sleep(100);
				reg.CheckPortExpiration();
				var resolved = reg.Resolve(id);

				Assert.IsNotNull(resolved);
				Assert.AreEqual(1, reg.Count);
			}
		}

		[Test]
		public void PortAutomaticallyExpiresAfter20Seconds()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 10;
				var id = reg.Register(new Port<string>());
				Thread.Sleep(21000);
				GC.Collect();
				var resolved = reg.Resolve(id);

				Assert.IsNull(resolved);
			}
		}

		[Test]
		public void ResolvingPortRenewsExpirationDate()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 3;
				var id = reg.Register(new Port<string>());
				Thread.Sleep(2000);
				reg.Resolve(id);
                reg.CheckPortExpiration();
				GC.Collect();
				var resolved = reg.Resolve(id);
				Assert.IsNotNull(resolved);
			}
		}

		[Test]
		public void GarbageCollectedWeakRefIsRemoved()
		{
			using (var reg = new RemotePortRegistry())
			{
				reg.LeaseTimeSeconds = 0;
				var id = reg.Register(new Port<string>());
				Thread.Sleep(100);
				reg.CheckPortExpiration();
				GC.Collect();
				reg.CheckPortExpiration();
				var resolved = reg.Resolve(id);
				
				Assert.IsNull(resolved);
				Assert.AreEqual(0, reg.Count);
			}
		}
	}
}
